package com.textcheck;

/**
 * 编辑距离（Levenshtein Distance）算法实现类
 * 
 * 编辑距离是指两个字符串之间，由一个转换成另一个所需的最少编辑操作次数。
 * 允许的编辑操作包括：
 * 1. 插入一个字符
 * 2. 删除一个字符
 * 3. 替换一个字符
 * 
 * 算法特点：
 * - 基于动态规划
 * - 考虑字符的位置关系
 * - 适合短文本比较
 * - 对字符级别的变化敏感
 * 
 * 相似度计算：
 * similarity = 1 - (编辑距离 / 最大可能距离)
 * 其中最大可能距离通常取两个字符串长度的最大值
 * 
 * 优化：
 * - 为中文字符设置不同的操作成本
 * - 对短文本采用更严格的评分标准
 * - 考虑字符类型的权重
 */
public class LevenshteinDistance implements SimilarityCalculator {

    @Override
    public double calculate(String text1, String text2) {
        if (text1 == null || text2 == null) {
            return 0.0;
        }

        // 处理空字符串的特殊情况
        if (text1.isEmpty() && text2.isEmpty()) {
            return 1.0;
        }
        if (text1.isEmpty() || text2.isEmpty()) {
            return 0.0;
        }

        // 预处理文本
        text1 = normalizeText(text1);
        text2 = normalizeText(text2);

        int len1 = text1.length();
        int len2 = text2.length();

        // 如果字符串完全相同
        if (text1.equals(text2)) {
            return 1.0;
        }

        // 计算长度比例因子，用于调整相似度
        double lengthRatio = (double) Math.min(len1, len2) / Math.max(len1, len2);

        // 创建动态规划矩阵
        double[][] dp = new double[len1 + 1][len2 + 1];

        // 初始化矩阵第一行和第一列
        for (int i = 0; i <= len1; i++) {
            dp[i][0] = i;
        }
        for (int j = 0; j <= len2; j++) {
            dp[0][j] = j;
        }

        // 动态规划计算编辑距离
        for (int i = 1; i <= len1; i++) {
            char c1 = text1.charAt(i - 1);
            for (int j = 1; j <= len2; j++) {
                char c2 = text2.charAt(j - 1);
                
                if (c1 == c2) {
                    // 字符相同，无需编辑
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    // 计算三种操作的成本
                    double replaceCost = getReplaceCost(c1, c2);
                    double deleteCost = getDeleteCost(c1);
                    double insertCost = getInsertCost(c2);
                    
                    dp[i][j] = Math.min(
                        Math.min(
                            dp[i - 1][j] + deleteCost,     // 删除
                            dp[i][j - 1] + insertCost      // 插入
                        ),
                        dp[i - 1][j - 1] + replaceCost     // 替换
                    );
                }
            }
        }

        // 获取最终的编辑距离
        double editDistance = dp[len1][len2];

        // 计算基础相似度
        double maxPossibleDistance = Math.max(len1, len2);
        double baseSimilarity = 1.0 - (editDistance / maxPossibleDistance);

        // 根据文本长度和特性调整相似度
        double adjustedSimilarity;
        if (Math.min(len1, len2) < 5) {
            // 短文本的相似度调整
            adjustedSimilarity = baseSimilarity * (0.7 + 0.3 * lengthRatio);
        } else if (allChinese(text1) && allChinese(text2)) {
            // 纯中文文本的相似度调整
            adjustedSimilarity = baseSimilarity * (0.9 + 0.1 * lengthRatio);
        } else {
            // 混合文本的相似度调整
            adjustedSimilarity = baseSimilarity * (0.8 + 0.2 * lengthRatio);
        }

        return Math.max(0.0, Math.min(1.0, adjustedSimilarity));
    }

    /**
     * 文本标准化处理
     */
    private String normalizeText(String text) {
        return text.trim().replaceAll("\\s+", " ");
    }

    /**
     * 获取字符替换操作的成本
     * 根据字符类型（中文/非中文）设置不同的成本
     */
    private double getReplaceCost(char c1, char c2) {
        if (isChinese(c1) && isChinese(c2)) {
            return 0.8; // 中文字符间的替换成本较低
        }
        if (!isChinese(c1) && !isChinese(c2)) {
            return 0.6; // 非中文字符间的替换成本更低
        }
        return 1.0; // 中文和非中文字符间的替换成本较高
    }

    /**
     * 获取删除操作的成本
     */
    private double getDeleteCost(char c) {
        return isChinese(c) ? 1.0 : 0.8;
    }

    /**
     * 获取插入操作的成本
     */
    private double getInsertCost(char c) {
        return isChinese(c) ? 1.0 : 0.8;
    }

    /**
     * 判断字符是否为中文
     */
    private boolean isChinese(char c) {
        Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
        return ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
            || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
            || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B;
    }

    /**
     * 判断文本是否全为中文（忽略空白字符）
     */
    private boolean allChinese(String text) {
        for (char c : text.toCharArray()) {
            if (!isChinese(c) && !Character.isWhitespace(c)) {
                return false;
            }
        }
        return true;
    }
}